using System;
using System.Collections.Generic;
using System.Linq;
using MoonFramework.Scripts.Mono;
using MoonFramework.Scripts.Pool;
using MoonFramework.Template;
using UnityEngine;

namespace MoonFramework.Scripts.Tool.Timer
{
    /// <summary>
    /// Unity主线程定时器（帧驱动版）
    /// </summary>
    public class Timer : BaseManager<Timer>
    {
        #region 数据结构
        private class TimerTask : IComparable<TimerTask>
        {
            public int TaskId;
            public float ExecuteTime;
            public float Interval;
            public int RepeatCount;
            public Action Callback;
            public int CurrentCount;
            public bool UseUnscaledTime;

            public void Init(int taskId, float executeTime, TimeSpan interval, int repeatCount,
                Action callback, bool useUnscaledTime)
            {
                TaskId = taskId;
                ExecuteTime = executeTime;
                Interval = (float)interval.TotalMilliseconds;
                RepeatCount = repeatCount;
                Callback = callback;
                UseUnscaledTime = useUnscaledTime;
                CurrentCount = 0;
            }

            public int CompareTo(TimerTask other)
            {
                return Mathf.Approximately(ExecuteTime, other.ExecuteTime)
                    ? TaskId.CompareTo(other.TaskId)
                    : ExecuteTime.CompareTo(other.ExecuteTime);
            }

            public void ReturnPool()
            {
                TaskId = 0;
                Callback = null;
                ExecuteTime = 0;
                RepeatCount = 0;
                CurrentCount = 0;
                Interval = 0f;
                this.MoonObjPushPool();
            }
        }
        #endregion

        #region 核心字段
        private readonly SortedSet<TimerTask> _taskSet = new();
        private readonly Dictionary<int, TimerTask> _taskMap = new();
        private int _taskCounter;
        #endregion

        #region Unity生命周期
        public void Init()
        {
            MonoManager.Instance.SendUpdateEvent(OnUpdate);
        }

        private void OnUpdate()
        {
            ProcessTasks(Time.time, Time.unscaledTime);
        }
        #endregion

        #region 公开接口
        /// <summary>
        /// 添加定时任务（使用缩放时间）
        /// </summary>
        /// <param name="delay">初始延迟时间</param>
        /// <param name="interval">重复间隔（0表示不重复）</param>
        public int AddTask(Action callback,
            TimeSpan interval = default, int repeatCount = 0)
        {
            return AddTaskInternal(callback, interval, repeatCount, false);
        }

        /// <summary>
        /// 添加定时任务（使用真实时间）
        /// </summary>
        public int AddUnscaledTask(Action callback,
            TimeSpan interval = default, int repeatCount = 0)
        {
            return AddTaskInternal(callback, interval, repeatCount, true);
        }

        /// <summary>
        /// 取消定时任务
        /// </summary>
        public bool CancelTask(int taskId)
        {
            return _taskMap.Remove(taskId);
        }
        #endregion

        #region 核心实现
        private int AddTaskInternal(Action callback, TimeSpan interval,
            int repeatCount, bool useUnscaled)
        {
            var currentTime = useUnscaled ? Time.unscaledTime : Time.time;
            var task = ObjPoolManager.Instance.Pop(() => new TimerTask());

            task.Init(
                taskId: ++_taskCounter,
                executeTime: currentTime + (float)interval.TotalMilliseconds,
                interval: interval,
                repeatCount: repeatCount,
                callback: callback,
                useUnscaledTime: useUnscaled
            );

            _taskSet.Add(task);
            _taskMap.Add(task.TaskId, task);
            return task.TaskId;
        }

        private void ProcessTasks(float scaledTime, float unscaledTime)
        {
            while (_taskSet.Count > 0)
            {
                var task = _taskSet.First();
                float currentTime = task.UseUnscaledTime ? unscaledTime : scaledTime;

                if (task.ExecuteTime > currentTime * 1000) break;
                bool exist = _taskMap.ContainsKey(task.TaskId);
                if(exist) ExecuteTask(task);
                if (exist && ShouldReschedule(task))
                {
                    task.ExecuteTime = currentTime + task.Interval;
                }
                else
                {
                    _taskSet.Remove(task);
                    CleanupTask(task);
                }
            }
        }

        private static void ExecuteTask(TimerTask task)
        {
            try
            {
                task.Callback?.Invoke();
                task.CurrentCount++;
            }
            catch (Exception ex)
            {
                Serilog.Log.Error(
                    $"Timer task #{task.TaskId} " +
                    $"(execution {task.CurrentCount + 1}/{task.RepeatCount}) " +
                    $"failed: {ex}"
                );
            }
        }

        private void CleanupTask(TimerTask task)
        {
            _taskMap.Remove(task.TaskId);
            task.ReturnPool();
        }

        private static bool ShouldReschedule(TimerTask task)
        {
            return task.Interval > 0 &&
                   (task.RepeatCount == 0 || task.CurrentCount < task.RepeatCount);
        }
        #endregion
    }
}
